using System;
using System.Data;
using System.Xml;
using FormsGenerator.FormsGeneratorException;

namespace FormsGenerator.DataAccess
{
    /// <summary>
    /// Summary description for DBFileHandler.
    /// </summary>
    public class DBFileHandler : IDBHandler
    {
        #region IDBHandler Members

        public void SaveForm(string strGUID, string formName, XmlDocument formXmlDoc, string strUserId)
        {
            Guid newGuid = new Guid(strGUID);

            DataTable dt = GetFormStatus(newGuid, strUserId);

            if (dt.Rows.Count == 0)
            {
                throw new ApplicationException(
                    "Error saving form! Either the form does not exists or you do not have permission to save the form.");
            }
            if (dt.Rows[0]["LockedBy"].Equals(DBNull.Value) || ((string) dt.Rows[0]["LockedBy"]).Trim().Equals(""))
            {
                throw new ApplicationException("Cannot save form. Form is not locked by you!");
            }
                //User can only save the form which was locked by the user.
            if (((string) dt.Rows[0]["LockedBy"]).Trim().ToLower().Equals(strUserId.ToLower()))
            {
                DataTable forms = FormFiles.Load();
                DataRow[] formRows = forms.Select("guid = '" + strGUID + "'");
                if (formRows.Length == 1)
                {
                    formRows[0]["Name"] = formName;
                    formRows[0]["XML"] = formXmlDoc.InnerXml;
                }
                else
                {
                    throw new ApplicationException("There was an error updating the Form!");
                }

                if (!FormFiles.SaveForm(newGuid))
                {
                    throw new ApplicationException("There was an error updating the Form!");
                }
            }
            else
            {
                throw new PermissionDeniedException("The form is locked by user " + (string) dt.Rows[0]["LockedBy"] +
                                                    ", since " + dt.Rows[0]["LockedSince"] +
                                                    ", and you cannot save a form which is not locked by you!");
            }
        }


        public DataTable CreateForm(string formName, XmlDocument formXmlDoc, string owner)
        {
            Guid guid = Guid.NewGuid();
            DateTime now = DateTime.Now;
            DataRow newRow = FormFiles.AddFormRow(guid, formName, now, owner, formXmlDoc.InnerXml, owner, now);
            FormFiles.SaveForm(guid);

            DataTable dt = FormFiles.CreateDataTableStructure();
            dt.ImportRow(newRow);

            return dt;
        }

        public DataTable GetForm(string strGUID)
        {
            DataTable forms = FormFiles.Load();

            DataRow[] form = forms.Select("guid = '" + strGUID + "'");

            DataTable dt = FormFiles.CreateDataTableStructure();

            if (form.Length == 1)
            {
                dt.ImportRow(form[0]);
            }

            return dt;
        }

        public DataTable GetForms(string strUserId)
        {
            return FormFiles.Load();
        }

        public void LockForm(string strGUID, string strUserId)
        {
            Guid newGuid = new Guid(strGUID);


            DataTable dt = GetFormStatus(newGuid, strUserId);

            if (dt.Rows.Count == 0)
            {
                throw new ApplicationException(
                    "Error locking form! Either the form does not exists or you do not have permission to lock the form.");
            }
            if (dt.Rows[0]["LockedBy"].Equals(DBNull.Value) || ((string) dt.Rows[0]["LockedBy"]).Trim().Equals(""))
            {
                DataTable forms = FormFiles.Load();

                DataRow[] form = forms.Select("guid = '" + strGUID + "'");
                if (form.Length == 1)
                {
                    form[0]["LockedBy"] = strUserId;
                    form[0]["LockedSince"] = DateTime.Now;
                }

                if (!FormFiles.SaveForm(newGuid))
                {
                    throw new ApplicationException(
                        "There was an error locking the Form! Either you do not have permission to lock the form or there was a database error");
                }
            }
            else
            {
                throw new PermissionDeniedException("The form is locked by user " + (string) dt.Rows[0]["LockedBy"] +
                                                    ", since " + dt.Rows[0]["LockedSince"]);
            }
        }

        public void UnLockForm(string strGUID, string strUserId)
        {
            Guid newGuid = new Guid(strGUID);

            DataTable dt = GetFormStatus(newGuid, strUserId);

            if (dt.Rows.Count == 0)
            {
                throw new ApplicationException(
                    "Error Unlocking form! Either the form does not exists or you do not have permission to unlock the form.");
            }
            if (dt.Rows[0]["LockedBy"].Equals(DBNull.Value) || ((string) dt.Rows[0]["LockedBy"]).Trim().Equals(""))
            {
                throw new ApplicationException("Form is not locked!");
            }
                //User can only unlock the form which was locked by the user.
            if (((string) dt.Rows[0]["LockedBy"]).Trim().ToLower().Equals(strUserId.ToLower()))
            {
                DataTable forms = FormFiles.Load();

                DataRow[] form = forms.Select("guid = '" + strGUID + "'");
                if (form.Length == 1)
                {
                    form[0]["LockedBy"] = "";
                    form[0]["LockedSince"] = DBNull.Value;
                }

//TODO: if we do not find the form throw exception! need to do this in all places where we do selects

                if (!FormFiles.SaveForm(newGuid))
                {
                    throw new ApplicationException("There was an error unlocking the Form!");
                }
            }
            else
            {
                throw new PermissionDeniedException("The form is locked by user " + (string) dt.Rows[0]["LockedBy"] +
                                                    ", since " + dt.Rows[0]["LockedSince"] +
                                                    ", and you cannot unlock a form which is not locked by you!");
            }
        }

        public void DeleteForm(string strGUID, string strUserId)
        {
            Guid newGuid = new Guid(strGUID);

            DataTable dt = GetFormStatus(newGuid, strUserId);

            if (dt.Rows.Count == 0)
            {
                throw new ApplicationException(
                    "Error Deleting form! Either the form does not exists or you do not have permission to delete the form.");
            }
            if (dt.Rows[0]["LockedBy"].Equals(DBNull.Value) || ((string) dt.Rows[0]["LockedBy"]).Trim().Equals(""))
            {
                throw new ApplicationException("Form is not locked!");
            }
            if (!((string) dt.Rows[0]["Owner"]).Trim().ToLower().Equals(strUserId.ToLower()))
            {
                throw new ApplicationException("You cannot delete a form that is not owned by you!");
            }
                //User can only delete the form which was locked by the user.
            if (((string) dt.Rows[0]["LockedBy"]).Trim().ToLower().Equals(strUserId.ToLower()))
            {
                DataTable forms = FormFiles.Load();

                DataRow[] form = forms.Select("guid = '" + strGUID + "'");
                if (form.Length == 1)
                {
                    //delete all formaccess entries for this guid:
                    RemoveFormAccess(strGUID, strUserId, string.Empty);
                    //then delete the form itself
                    FormFiles.RemoveFormRow(newGuid);
                    FormFiles.SaveAllForms();
                }
            }
            else
            {
                throw new PermissionDeniedException("The form is locked by user " + (string) dt.Rows[0]["LockedBy"] +
                                                    ", since " + dt.Rows[0]["LockedSince"] +
                                                    ", and you cannot delete a form which is not locked by you!");
            }
        }

        public DataTable GetFormStatus(Guid guid, string strUserId)
        {
            DataTable access = FormSettings.Load();
            DataTable forms = FormFiles.Load();

            DataRow[] accessRows = access.Select("UserId = '" + strUserId + "' OR UserId = 'all'");

            string filter = "(guid = '" + guid + "')";
            if (accessRows.Length > 0)
            {
                //has indirect access
            }
            else
            {
                //do not have indirect access, check if the user is owner:
                filter += " AND (Owner Like '" + strUserId + "')";
            }

            DataRow[] guidForm = forms.Select(filter);
            DataTable dt = FormFiles.CreateDataTableStructure();
            //should be only one:
            if (guidForm.Length == 1)
            {
                dt.ImportRow(guidForm[0]);
            }
            return dt;
        }

        public DataTable GetFormAccess(string strGUID, string strUserId)
        {
            DataTable access = FormSettings.Load();
            DataTable forms = FormFiles.Load();

            var dtCombined = new DataTable("Combined");
            DataColumn newColumn;
            foreach (DataColumn column in forms.Columns)
            {
                if (column.ColumnName != "guid") //needs to exclude this here as the next foreach will include this one.
                {
                    newColumn = dtCombined.Columns.Add(column.ColumnName, column.DataType);
                    newColumn.AllowDBNull = column.AllowDBNull;
                }
            }
            foreach (DataColumn column in access.Columns)
            {
                newColumn = dtCombined.Columns.Add(column.ColumnName, column.DataType);
                newColumn.AllowDBNull = column.AllowDBNull;
            }
            var key = new DataColumn[2];
            key[0] = dtCombined.Columns["guid"];
            key[1] = dtCombined.Columns["FormsAccessId"];
            dtCombined.PrimaryKey = key;

            string filter = "guid = '" + strGUID + "'";
            string formsFilter = filter + " AND Owner = '" + strUserId + "'";
            DataRow[] formRows = forms.Select(formsFilter);
            DataRow[] accessRows = access.Select(filter);

            foreach (DataRow accessRow in accessRows)
            {
                foreach (DataRow formRow in formRows)
                {
                    if (accessRow["guid"].ToString().Equals(formRow["guid"].ToString()))
                    {
                        //Name, CreatedDate, Owner, XML, LockedBy, LockedSince, FormsAccessId, guid, UserId
                        var values = new object[formRow.ItemArray.Length + accessRow.ItemArray.Length - 1];
                        int position = 0;
                        foreach (object formValue in formRow.ItemArray)
                        {
                            if (!(formValue is Guid))
                            {
                                values[position] = formValue;
                                position ++;
                            }
                        }
                        foreach (object accessValue in accessRow.ItemArray)
                        {
                            values[position] = accessValue;
                            position ++;
                        }
                        dtCombined.Rows.Add(values);
                    }
                }
            }

            return dtCombined;
        }

        public void RemoveFormAccess(string strGUID, string strUserId, string strUserToBeRemovedId)
        {
            //if strUserToBeRemovedId is empty; delete all access points for the selected gui.
            Guid newGuid = new Guid(strGUID);

            DataTable dt = GetFormStatus(newGuid, strUserId);

            if (dt.Rows.Count == 0)
            {
                throw new ApplicationException(
                    "Error removing user! Either the form does not exists or you do not have permission to edit form access.");
            }
            if (!((string) dt.Rows[0]["Owner"]).Trim().ToLower().Equals(strUserId.ToLower()))
            {
                throw new ApplicationException("You cannot edit access to a form that is not owned by you!");
            } //User can only remove form access which the user is the owner of.
            DataTable formAccess = FormSettings.Load();
            string filter = "guid = '" + strGUID + "'";
            if (!strUserToBeRemovedId.Equals(string.Empty))
            {
                filter += " AND UserId = '" + strUserToBeRemovedId + "'";
            }

            DataRow[] accessRows = formAccess.Select(filter);
            if (accessRows.Length > 0)
            {
                foreach (DataRow row in accessRows)
                {
                    FormSettings.Remove(int.Parse(row["FormsAccessId"].ToString()));
                }
                FormSettings.Save();
            }
        }

        public void UpdateFormAccess(string strGUID, string strUserId, string strUserToUpdateId, string strFormsAccessId)
        {
            Guid newGuid = new Guid(strGUID);

            DataTable dt = GetFormStatus(newGuid, strUserId);

            if (dt.Rows.Count == 0)
            {
                throw new ApplicationException(
                    "Error updating user! Either the form does not exists or you do not have permission to edit form access.");
            }
            if (!((string) dt.Rows[0]["Owner"]).Trim().ToLower().Equals(strUserId.ToLower()))
            {
                throw new ApplicationException("You cannot edit access to a form that is not owned by you!");
            }
                //User can only remove form access which the user is the owner of.
            DataTable formAccess = FormSettings.Load();
            string filter = "FormsAccessId = " + strFormsAccessId + " AND guid = '" + strGUID + "'";

            DataRow[] accessRows = formAccess.Select(filter);
            if (accessRows.Length == 1)
            {
                accessRows[0]["UserId"] = strUserToUpdateId;
                if (!FormSettings.Save())
                {
                    throw new ApplicationException(
                        "There was an error updating form access! Either you do not have permission to edit the forms access or there was a database error");
                }
            }
            else
            {
                throw new ApplicationException(
                    "There was an error updating form access! Either you do not have permission to edit the forms access or there was a database error");
            }
        }

        public void InsertFormAccess(string strGUID, string strUserId, string strUserToAddId)
        {
            Guid newGuid = new Guid(strGUID);

            DataTable dt = GetFormStatus(newGuid, strUserId);

            if (dt.Rows.Count == 0)
            {
                throw new ApplicationException(
                    "Error adding user! Either the form does not exists or you do not have permission to edit form access.");
            }
            if (!((string) dt.Rows[0]["Owner"]).Trim().ToLower().Equals(strUserId.ToLower()))
            {
                throw new ApplicationException("You cannot edit access to a form that is not owned by you!");
            }
                //User can only remove form access which the user is the owner of.
            FormSettings.Load();
            FormSettings.AddAccessRow(newGuid, strUserToAddId);

            if (!FormSettings.Save())
            {
                throw new ApplicationException(
                    "There was an error updating form access! Either you do not have permission to edit the forms access or there was a database error");
            }
        }

        #endregion
    }
}